﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.IO;
using System.Web;
using System.Diagnostics;
using HtmlAgilityPack;
using System.Xml.Linq;

namespace NotaManager
{
    public class DafaultNotaManager : INotaManager
    {
        private bool _logged;

        private HtmlDocument _document;

        private CookieContainer _cookies;

        private string _loginTemplate = @"User[login]={0}&User[pass]={1}";

        private string _translationTextTemplate = @"Translation[body]={0}&ajax=1";

        private string _loginURL = @"http://notabenoid.ru/register/login";

        //private string _indexURL = @"http://notabenoid.ru/index.php";

        private string _bookURL = @"http://notabenoid.ru/book/54445";

        private string _chapterURL = @"http://notabenoid.ru/book/54445/{0}";

        private string _translateURL = @"http://notabenoid.ru/book/54445/{0}/{1}/translate";

        public DafaultNotaManager()
        {
            _cookies = new CookieContainer();
            _logged = false;
            _document = new HtmlDocument();
        }


        async public Task<LoginResult> Login(string username, string password)
        {
            try
            {
                var loginData = string.Format(_loginTemplate, username, password);
                var responce = await post(_loginURL, loginData);

                if (responce.ResponseUri.AbsoluteUri != "http://notabenoid.ru/index.php")
                {
                    Debug.WriteLine("Server refuses connection");
                    return new LoginResult { Status = RequestStatus.ServerRefuse };
                }

                //await get(_indexURL);

                _logged = true;
                return new LoginResult { Status = RequestStatus.Sucessful };
            }
            catch (Exception e)
            {

                return new LoginResult { Status = RequestStatus.ConnectionError, Exception = e };
            }
        }

        public void Logout()
        {
            _logged = false;
        }

        async public Task<Script> GetTranslation(Action<double> callback = null)
        {
            if (!_logged)
            {
                throw new InvalidOperationException("You must login first!");
            }

            var script = new Script();
            script.Files = new List<File>();

            var bookResponce = await get(_bookURL);
            var responceStream = bookResponce.GetResponseStream();

            _document.Load(responceStream, Encoding.UTF8);
            var root = _document.GetElementbyId("Chapters");
            var chapters = root.
                Element("tbody").
                Elements("tr").
                Select(
                tr => {
                    var a = tr.FirstChild.Element("a");
                    var title = a.InnerText;
                    var id = a.Attributes.First(attr => attr.Name == "href").Value.Split('/').Last();
                    return new { Title = title, Id = id };
                }
                    ).ToList();


            int counter = 1;
            int count = chapters.Count;

            foreach (var chapter in chapters)
            {
                if (chapter.Id == "239108")
                {
                    break;
                }

                await loadChapter(chapter.Id, chapter.Title, script);

                if (callback != null)
                {
                    var percent = (double)counter / (double)count;
                    callback(percent * 100);
                }
                
                counter++;
            }


            return script;
        }

        async public Task Post(NewTranslation translation)
        {
            var url = string.Format(_translateURL, translation.TargetFileNotaID, translation.TargetLineNotaID);
            var data = string.Format(_translationTextTemplate, translation.Value);

            await post(url, data);
        }

        public void Post(List<NewTranslation> translations)
        {
            throw new NotImplementedException();
        }

        async private Task loadChapter(string chapterId, string chapterTitle, Script script)
        {
            var chapterURL = string.Format(_chapterURL, chapterId);
            var file = new File();
            file.Title = chapterTitle;
            file.NotaID = chapterId;
            file.Lines = new List<Line>();

            var chapterData = await get(chapterURL);
            var temp = new HtmlDocument();
            temp.Load(chapterData.GetResponseStream(), Encoding.UTF8);

            var pagesContainer = temp.GetElementbyId("tb-main").
                Descendants("div").ElementWithAttribute("class", "chic-pages");

            var pages = new List<string>();
            if (pagesContainer != null)
            {
                pages.AddRange(pagesContainer.Elements("ul").ElementWithAttribute("class", "selectable").
                Descendants("a").
                Select(
                    a =>
                    {
                        return a.Attributes.First(attr => attr.Name == "href").Value;
                    }
                ).ToList());
            }

            loadChapter(temp, file);

            foreach (var page in pages)
            {
                chapterData = await get(chapterURL + page);
                temp.Load(chapterData.GetResponseStream(), Encoding.UTF8);

                loadChapter(temp, file);
            }

            script.Files.Add(file);
        }

        private void loadChapter(HtmlDocument doc, File file)
        {
            var trs = doc.GetElementbyId("Tr").
                Element("tbody").
                Elements("tr");

            var lines = trs.
                Select(tr =>
                {
                    var tds = tr.Elements("td");
                    var original = tds.ElementWithAttribute("class", "o").
                        Descendants("p").
                        ElementWithAttribute("class", "text").
                        InnerHtml.Replace("<br>", "ð").Split('ð');

                    var translates = tds.ElementWithAttribute("class", "t").
                        Elements("div").
                        Select(translate =>
                        {
                            var text = translate.Descendants("p").ElementWithAttribute("class", "text").InnerHtml;
                            

                            var rate = translate.Descendants("div").ElementWithAttribute("class", "rating").
                                Elements("a").Last().InnerText;

                            var autor = translate.Descendants("a").ElementWithAttribute("class", "user").
                                InnerText;

                            var date = translate.Descendants("p").ElementWithAttribute("class", "info").
                                ChildNodes.
                                Last(node => node.NodeType == HtmlNodeType.Text).InnerText.Trim();

                            // Формат даты
                            // 27.08.14 в 19:31
                            var clrDate = DateTime.ParseExact(date, "d.MM.yy в H:mm", null);

                            return new RatedString { Value = text, Rating = int.Parse(rate), Date = clrDate, Translator = autor };

                        });

                    var id = tr.Attributes.First(attr => attr.Name == "id").Value;

                    var eng = original[1].Replace("\r", "").Replace("\n", "");

                    var jap = original[0];

                    return new Line { Jap = jap, Eng = eng, Rus = translates.ToList(), NotaID = id.Substring(1, id.Length - 1) };
                }
                );

            file.Lines.AddRange(lines);
        }

        

        async private Task<HttpWebResponse> get(string url)
        {
            var request = WebRequest.CreateHttp(url);
            request.CookieContainer = _cookies;
            request.Proxy = null;// new WebProxy("127.0.0.1", 8888);

            var responce = (HttpWebResponse)await request.GetResponseAsync();
            return responce;
        }

        async private Task<HttpWebResponse> post(string url, string data, bool updateCookies = false)
        {
            var request = WebRequest.CreateHttp(url);
            request.CookieContainer = _cookies;

            request.Proxy = null;// new WebProxy("127.0.0.1", 8888);
            request.Method = "POST";
            request.ContentType = @"application/x-www-form-urlencoded; charset=UTF-8";
            request.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore);

            var encodedData = data.Replace("[", "%5B").Replace("]", "%5D");
            var content = Encoding.UTF8.GetBytes(encodedData);
            var contentLength = content.Length;
            request.ContentLength = contentLength;
            using (var temp = await request.GetRequestStreamAsync())
            {
                await temp.WriteAsync(content, 0, contentLength);
            }

            var responce = (HttpWebResponse)await request.GetResponseAsync();
            if (updateCookies)
            {
                //_cookies = responce.Cookies.;
            }

            return responce;
        }

        public List<Line> FindLines(string text)
        {
            throw new NotImplementedException();
        }
    }
}
